﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using Casanova.Math;

namespace StudentsAssignment
{
    enum Goals
    {
        StopHunger,
    }

    enum PlannerPreconditionType
    {
        Location,
        Groceries,
        Money,
    }

    enum PlannerLocationType
    {
        Home,
        Store,
        Work,
        Park,
        Cinema,
    }

    enum PlannerActionEffectType
    {
        SatedChange,
        GroceriesChange,
        MoneyChange,
        LocationChange,
        RestedChange,
        RelaxedChange,
        EnjoyedEffect,
        FitChange
    }

    enum PlannerActionType
    {
        HaveSnack,
        Buy,
        Root,
        GoTo,
        Work,
        Sleep,
        WatchTV,
        Walk,
        CookAndEat,
        WatchMovie,
    }

    class PlannerActionEffect
    {
        public PlannerActionEffectType type;

        public float sated_effect;

        internal static PlannerActionEffect CreateSatedChange(float p)
        {
            PlannerActionEffect effect = new PlannerActionEffect();
            effect.type = PlannerActionEffectType.SatedChange;
            effect.sated_effect = p;
            
            return effect;
        }

        internal static PlannerActionEffect CreateGroceriesChange(int p)
        {
            PlannerActionEffect effect = new PlannerActionEffect();
            effect.type = PlannerActionEffectType.GroceriesChange;
            effect.grocieries_effect = p;

            return effect;
        }

        public int grocieries_effect { get; set; }

        internal static PlannerActionEffect MoneyChange(int p)
        {
            PlannerActionEffect effect = new PlannerActionEffect();
            effect.type = PlannerActionEffectType.MoneyChange;
            effect.money_effect = p;

            return effect;
        }

        public int money_effect { get; set; }

        internal static PlannerActionEffect CreateLocationChange(PlannerLocationType plannerLocationType)
        {
            PlannerActionEffect effect = new PlannerActionEffect();
            effect.type = PlannerActionEffectType.LocationChange;
            effect.location_target = plannerLocationType;

            return effect;
        }

        public PlannerLocationType location_target { get; set; }

        internal static PlannerActionEffect CreateRestedChange(float p)
        {
            PlannerActionEffect effect = new PlannerActionEffect();
            effect.type = PlannerActionEffectType.RestedChange;
            effect.rested_effect = p;

            return effect;
        }

        public float rested_effect { get; set; }

        internal static PlannerActionEffect CreateRelaxedEffect(float p)
        {
            PlannerActionEffect effect = new PlannerActionEffect();
            effect.type = PlannerActionEffectType.RelaxedChange;
            effect.relaxed_effect = p;

            return effect;
        }

        public float relaxed_effect { get; set; }

        internal static PlannerActionEffect CreateEnjoyedEffect(float p)
        {
            PlannerActionEffect effect = new PlannerActionEffect();
            effect.type = PlannerActionEffectType.EnjoyedEffect;
            effect.enjoyed_effect = p;

            return effect;
        }

        public float enjoyed_effect { get; set; }

        public float fit_effect { get; set; }

        internal static PlannerActionEffect CreateFitEffect(float p)
        {
            PlannerActionEffect effect = new PlannerActionEffect();
            effect.type = PlannerActionEffectType.FitChange;
            effect.fit_effect = p;

            return effect;
        }
    }

    class PlannerPrecondition
    {
        
        public PlannerPreconditionType type;
        
        // type == Location
        public PlannerLocationType location;

        // type == Groceries
        public int min_groceries_level;

        // type == Money
        public int min_amount_money { get; set; }

        public PlannerPrecondition()
        {
        }

        internal static PlannerPrecondition CreateGroceries(int p)
        {
            PlannerPrecondition precond = new PlannerPrecondition();
            precond.type = PlannerPreconditionType.Groceries;
            precond.min_groceries_level = p;

            return precond;
        }

        internal static PlannerPrecondition CreateLocation(PlannerLocationType plannerLocationType)
        {
            PlannerPrecondition precond = new PlannerPrecondition();
            precond.type = PlannerPreconditionType.Location;
            precond.location = plannerLocationType;

            return precond;
        }

        internal static PlannerPrecondition CreateMoney(int amount_money)
        {
            PlannerPrecondition precond = new PlannerPrecondition();
            precond.type = PlannerPreconditionType.Money;
            precond.min_amount_money = amount_money;

            return precond;
        }
    }

    class PlannerAction
    {
        public List<PlannerActionEffect> effects;
        public List<PlannerPrecondition> preconditions;
        public PlannerActionType type;

        public PlannerAction(PlannerActionType type)
        {
            this.type = type;
            this.preconditions = new List<PlannerPrecondition>();
            this.effects = new List<PlannerActionEffect>();
        }
    }

    struct PlannerWorldState
    {
        public List<PlannerPrecondition> preconditions;

        public PlannerActionEffect goal { get; set; }

        public float sated { get; set; }

        internal static PlannerWorldState UpdateStateWithAction( PlannerNode node, PlannerWorldState current_state, PlannerAction possible_action)
        {
            PlannerWorldState new_state = current_state;

            // add the effects to the world state
            for (int i = 0; i < possible_action.effects.Count; ++i)
            {
                PlannerActionEffect effect = possible_action.effects[i];

                if (effect.type == PlannerActionEffectType.SatedChange)
                {
                    new_state.sated += effect.sated_effect;
                }
                else if (effect.type == PlannerActionEffectType.MoneyChange)
                {
                    new_state.money += effect.money_effect;
                }
                else if (effect.type == PlannerActionEffectType.GroceriesChange)
                {
                    new_state.groceries += effect.grocieries_effect;
                }
                else if (effect.type == PlannerActionEffectType.LocationChange)
                {
                    new_state.location = effect.location_target;
                }
                else if (effect.type == PlannerActionEffectType.RestedChange)
                {
                    new_state.rested += effect.rested_effect;
                }
                else if (effect.type == PlannerActionEffectType.RelaxedChange)
                {
                    new_state.relaxed += effect.relaxed_effect;
                }
                else if (effect.type == PlannerActionEffectType.EnjoyedEffect)
                {
                    new_state.enjoyed += effect.enjoyed_effect;
                }
                else if (effect.type == PlannerActionEffectType.FitChange)
                {
                    new_state.fit += effect.fit_effect;
                }
                else
                {
                    Debug.Assert(false, "effect type not yet supported " + effect);
                }
            }

            new_state.preconditions = new List<PlannerPrecondition>();

            // copy preconditions into the new structure
            for (int i = 0; i < current_state.preconditions.Count; ++i)
            {
                PlannerPrecondition precondition = current_state.preconditions[i];

                // check if the preconditions have been solved
                if (precondition.type == PlannerPreconditionType.Location )
                {
                    // reached the location target
                    // precondition is solved 
                    // and doesn't need to be added again
                    if (new_state.location == precondition.location) continue;
                } else if (precondition.type == PlannerPreconditionType.Groceries ) {
                    if( new_state.groceries >= 0) continue;
                }
                else if (precondition.type == PlannerPreconditionType.Money )
                {
                    if( new_state.money >= 0 ) continue;
                }
                else
                {
                    Debug.Assert(false, "precondition continue condition not defined");
                }

                new_state.preconditions.Add(current_state.preconditions[i]);
            }

            // and add the preconditions
            for (int i = 0; i < possible_action.preconditions.Count; ++i)
            {
                PlannerPrecondition precondition = possible_action.preconditions[i];

                if (precondition.type == PlannerPreconditionType.Groceries)
                {
                    if (new_state.groceries >= 0)
                    {
                        // no need to buy groceries, we still have enough
                        continue;
                    }
                }
                else if (precondition.type == PlannerPreconditionType.Location)
                {
                    if (new_state.location == precondition.location)
                    {
                        // no need to get to the location, we are already there
                        // continue;
                    }
                }
                else if (precondition.type == PlannerPreconditionType.Money)
                {
                    if (new_state.money >= 0)
                    {
                        continue;
                    }
                }
                else
                {
                    Debug.Assert(false, "precondition continue condition not defined");
                }
                new_state.preconditions.Add(precondition);
            }

            node.world_state = new_state;

            return new_state;
        }

        public int money { get; set; }

        public int groceries { get; set; }

        public PlannerLocationType location { get; set; }

        public float rested { get; set; }

        public float relaxed { get; set; }

        public float enjoyed { get; set; }

        public float fit { get; set; }

        public static PlannerWorldState GenerateCurrentState(VirtualCity.CityDescription.AgentDescription agent, VirtualCity.PlanDescription.CityExpectations city)
        {
            PlannerWorldState world_state = new PlannerWorldState();

            //world_state.goal = PlannerActionEffect.CreateSatedChange(+0.10f);
            //world_state.goal = PlannerActionEffect.MoneyChange(+10);
            //world_state.goal = PlannerActionEffect.CreateRestedChange(0.10f);
            //world_state.goal = PlannerActionEffect.CreateRelaxedEffect(+0.10f);
            //world_state.goal = PlannerActionEffect.CreateEnjoyedEffect(+0.10f);
            // world_state.goal = PlannerActionEffect.CreateFitEffect(+0.10f);

            world_state.sated = agent.Stats.Sated;
            world_state.money = (int)agent.Money;
            world_state.groceries = CalcNumberOfGroceries(agent);
            world_state.preconditions = new List<PlannerPrecondition>();
            world_state.location = FindAgentLocation(agent, city);
            world_state.rested = agent.Stats.Rested;
            world_state.relaxed = agent.Stats.Relaxed;
            world_state.enjoyed = agent.Stats.Enjoyed;
            world_state.fit = agent.Stats.Fit;

            return world_state;
        }

        public static PlannerLocationType FindAgentLocation(VirtualCity.CityDescription.AgentDescription agent, VirtualCity.PlanDescription.CityExpectations city)
        {
            // are we at home
            if (AtLocation(agent.Position, agent.Home.Position))
            {
                return PlannerLocationType.Home;
            }
            else if (AtLocation(agent.Position, agent.Work.Position))
            {
                return PlannerLocationType.Work;
            }

            // todo: add more detections
            for (int i = 0; i < city.Buildings.Length; ++i)
            {
                if( AtLocation(agent.Position, city.Buildings[i].Item2.Position) == false ) continue;

                // park
                int park_item_id = MovesSelector.TryFindItemByName(city, i, "Woods trail");
                int store_item_id = MovesSelector.TryFindItemByName(city, i, "Groceries");
                int cinema_item_id = MovesSelector.TryFindItemByName(city, i, "Cinema-screen");
                
                if (park_item_id >= 0 ) return PlannerLocationType.Park;
                if (cinema_item_id >= 0) return PlannerLocationType.Cinema;
                if (store_item_id >= 0)
                {
                    if (city.Buildings[i].Item1[store_item_id].OpenUse == true)
                    {
                        return PlannerLocationType.Store;
                    }
                }
            }


            Debug.Assert(false, "location could not be identified");
            return PlannerLocationType.Store;
        }

        public static bool AtLocation(Vector2 vector21, Vector2 vector22)
        {
            Vector2 diff = vector22 - vector21;
            if (diff.Length < 1.0f)
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        public static int CalcNumberOfGroceries(VirtualCity.CityDescription.AgentDescription agent)
        {
            int count = 0;

            for (int i = 0; i < agent.Inventory.Length; ++i)
            {
                if (agent.Inventory[i].Name == "Groceries")
                {
                    ++count;
                }
            }

            return count;
        }
    }

    class PlannerNode
    {
        public PlannerAction action;
        public PlannerWorldState world_state;

        public List<PlannerNode> childs;
        public PlannerNode parent;

        public PlannerNode(PlannerAction possible_action, PlannerNode _parent )
        {
            childs = new List<PlannerNode>();
            action = possible_action;
            this.parent = _parent;
            this.invalid_path = false;
        }

        public bool invalid_path { get; set; }
    }

    class GoalPlanner
    {
        public List<PlannerAction> action_list;
        public List<PlannerNode> plan_endpoints;
        private VirtualCity.CityDescription.AgentDescription agent;
        private VirtualCity.PlanDescription.CityExpectations city;

        public GoalPlanner(VirtualCity.CityDescription.AgentDescription agent, VirtualCity.PlanDescription.CityExpectations city)
        {
            this.action_list = new List<PlannerAction>();
            this.plan_endpoints = new List<PlannerNode>();

            this.city = city;

            // add all supported actions
            PlannerAction action_have_snack = new PlannerAction(PlannerActionType.HaveSnack);

            // to have a snack the agent has to be at home and have at least one Groceries
            action_have_snack.preconditions.Add(PlannerPrecondition.CreateLocation(PlannerLocationType.Home));
            action_have_snack.preconditions.Add(PlannerPrecondition.CreateGroceries(1));

            action_have_snack.effects.Add(PlannerActionEffect.CreateSatedChange(0.10f));
            action_have_snack.effects.Add(PlannerActionEffect.CreateGroceriesChange(-1));

            // PlannerAction
            PlannerAction buy_groceries = new PlannerAction(PlannerActionType.Buy);
            buy_groceries.preconditions.Add(PlannerPrecondition.CreateLocation(PlannerLocationType.Store));
            buy_groceries.preconditions.Add(PlannerPrecondition.CreateMoney(10));

            buy_groceries.effects.Add(PlannerActionEffect.CreateSatedChange(-0.01f));
            buy_groceries.effects.Add(PlannerActionEffect.MoneyChange(-10));
            buy_groceries.effects.Add(PlannerActionEffect.CreateGroceriesChange(+10));

            PlannerAction work = new PlannerAction(PlannerActionType.Work);
            work.preconditions.Add(PlannerPrecondition.CreateLocation(PlannerLocationType.Work));

            work.effects.Add(PlannerActionEffect.MoneyChange(+30));

            PlannerAction sleep = new PlannerAction(PlannerActionType.Sleep);
            sleep.preconditions.Add(PlannerPrecondition.CreateLocation(PlannerLocationType.Home));

            sleep.effects.Add(PlannerActionEffect.CreateRestedChange(+0.35f));
            sleep.effects.Add(PlannerActionEffect.CreateSatedChange(-0.05f));

            PlannerAction watch_tv = new PlannerAction(PlannerActionType.WatchTV);

            watch_tv.preconditions.Add(PlannerPrecondition.CreateLocation(PlannerLocationType.Home));

            watch_tv.effects.Add(PlannerActionEffect.CreateRelaxedEffect(+0.30f));
            watch_tv.effects.Add(PlannerActionEffect.CreateEnjoyedEffect(+0.60f));

            PlannerAction walk = new PlannerAction(PlannerActionType.Walk);

            walk.preconditions.Add(PlannerPrecondition.CreateLocation(PlannerLocationType.Park));

            walk.effects.Add(PlannerActionEffect.CreateFitEffect(+0.20f));
            walk.effects.Add(PlannerActionEffect.CreateEnjoyedEffect(+0.30f));

            PlannerAction cook_and_eat = new PlannerAction(PlannerActionType.CookAndEat);
            cook_and_eat.preconditions.Add(PlannerPrecondition.CreateLocation(PlannerLocationType.Home));
            cook_and_eat.preconditions.Add(PlannerPrecondition.CreateGroceries(3));

            cook_and_eat.effects.Add(PlannerActionEffect.CreateGroceriesChange(-3));
            cook_and_eat.effects.Add(PlannerActionEffect.CreateSatedChange(+.80f));
            cook_and_eat.effects.Add(PlannerActionEffect.CreateEnjoyedEffect(+0.20f));
            cook_and_eat.effects.Add(PlannerActionEffect.CreateRestedChange(-0.05f));

            PlannerAction watch_movie = new PlannerAction(PlannerActionType.WatchMovie);
            watch_movie.preconditions.Add(PlannerPrecondition.CreateLocation(PlannerLocationType.Cinema));
            watch_movie.preconditions.Add(PlannerPrecondition.CreateMoney(20));

            watch_movie.effects.Add(PlannerActionEffect.CreateRelaxedEffect(+0.4f));
            watch_movie.effects.Add(PlannerActionEffect.MoneyChange(-20));
            watch_movie.effects.Add(PlannerActionEffect.CreateEnjoyedEffect(+0.8f));

            action_list.Add(action_have_snack);
            action_list.Add(cook_and_eat);

            if (city.Clock.Time.Hour > 16 && city.Clock.Time.Hour < (21) )
            {
                action_list.Add(watch_movie);
            }

            if (city.Clock.Time.Hour > 8 && city.Clock.Time.Hour < 18)
            {
                action_list.Add(buy_groceries);
            }

            int id = FindBuildByPosition(city, agent.Work.Position);

            int work_start = 7;
            int work_end = 18;

            for (int i = 0; i < city.Buildings[id].Item1.Length; ++i)
            {
                for( int j = 0; j < city.Buildings[id].Item1[i].UseEffects.Length; ++j ) {
                    if (city.Buildings[id].Item1[i].UseEffects[j].MoneyGained > 0)
                    {
                        // found the work, copy the work hours
                        work_start = city.Buildings[id].Item1[i].UseSlots[0].Start.Hours;
                        work_end = city.Buildings[id].Item1[i].UseSlots[0].Start.Hours + city.Buildings[id].Item1[i].UseSlots[0].Duration.Hours;
                    }
                }
            }

            if (city.Clock.Time.Hour > work_start && city.Clock.Time.Hour < work_end )
            {
                action_list.Add(work);
            }

            action_list.Add(sleep);
            action_list.Add(watch_tv);
            if (city.Clock.Time.Hour > 4 && city.Clock.Time.Hour < 16)
            {
                action_list.Add(walk);
            }
        }

        private int FindBuildByPosition(VirtualCity.PlanDescription.CityExpectations city,Vector2 vector2)
        {
            for (int i = 0; i < city.Buildings.Length; ++i)
            {
                if ((city.Buildings[i].Item2.Position - vector2).Length < 1)
                {
                    return i;
                }
            }

            return -1;
        }

        public List<List<PlannerNode>> Execute(VirtualCity.CityDescription.AgentDescription agent, PathWorld path_world )
        {
            List<PlannerActionEffectType> tried_goals = new List<PlannerActionEffectType>();

            bool valid_goal_found = false;

            do
            {
                PlannerAction action = new PlannerAction(PlannerActionType.Root);
                PlannerNode parent = new PlannerNode(action, null);
                PlannerWorldState world_state = PlannerWorldState.GenerateCurrentState(agent, city );

                float lowest_state = 1.1f;
                PlannerActionEffectType type = PlannerActionEffectType.MoneyChange;


                // assume valid goal will be found
                valid_goal_found = true;

                // just execute the lowest state action
                if (FindGoalInList(tried_goals, PlannerActionEffectType.SatedChange ) == false && world_state.sated < lowest_state)
                {
                    type = PlannerActionEffectType.SatedChange;
                    lowest_state = world_state.sated;
                }

                if (FindGoalInList(tried_goals, PlannerActionEffectType.RestedChange) == false && world_state.rested < lowest_state)
                {
                    type = PlannerActionEffectType.RestedChange;
                    lowest_state = world_state.rested;
                }

                if (FindGoalInList(tried_goals, PlannerActionEffectType.RelaxedChange) == false && world_state.relaxed < lowest_state)
                {
                    type = PlannerActionEffectType.RelaxedChange;
                    lowest_state = world_state.relaxed;
                }

                if (FindGoalInList(tried_goals, PlannerActionEffectType.FitChange) == false && world_state.fit < lowest_state)
                {
                    type = PlannerActionEffectType.FitChange;
                    lowest_state = world_state.fit;
                }

                if (FindGoalInList(tried_goals, PlannerActionEffectType.EnjoyedEffect) == false && world_state.enjoyed < lowest_state)
                {
                    type = PlannerActionEffectType.EnjoyedEffect;
                    lowest_state = world_state.enjoyed;
                }

                if (FindGoalInList(tried_goals, PlannerActionEffectType.MoneyChange) == false && world_state.money < 10 && lowest_state > 0.8f )
                {
                    type = PlannerActionEffectType.MoneyChange;
                }

                switch (type)
                {
                    case PlannerActionEffectType.SatedChange:
                        world_state.goal = PlannerActionEffect.CreateSatedChange(1 - world_state.sated);
                        break;

                    case PlannerActionEffectType.RestedChange:
                        world_state.goal = PlannerActionEffect.CreateRestedChange(+0.1f);
                        break;

                    case PlannerActionEffectType.RelaxedChange:
                        world_state.goal = PlannerActionEffect.CreateRelaxedEffect(+0.1f);
                        break;

                    case PlannerActionEffectType.FitChange:
                        world_state.goal = PlannerActionEffect.CreateFitEffect(+0.1f);
                        break;

                    case PlannerActionEffectType.EnjoyedEffect:
                        world_state.goal = PlannerActionEffect.CreateEnjoyedEffect(+0.1f);
                        break;

                    case PlannerActionEffectType.MoneyChange:
                        world_state.goal = PlannerActionEffect.MoneyChange(+10);
                        break;

                    default:
                        Debug.Assert(false, "goal solving not yet support");
                        break;

                }

                // world_state.goal = PlannerActionEffect.CreateSatedChange(1 - world_state.sated);
                // world_state.goal = PlannerActionEffect.CreateEnjoyedEffect(0.8f);

                parent.world_state = world_state;

                if (IsActionAvailabletoSolveGoal(world_state.goal, action_list) == false)
                {
                    // return null;
                    valid_goal_found = false;
                    tried_goals.Add(world_state.goal.type);
                    continue;
                }

                AddNextNode(parent, world_state, world_state);

                // now find the endpoints of all possible plans
                this.plan_endpoints.Clear();
                FindEnpoint(parent);

                List<List<PlannerNode>> output = new List<List<PlannerNode>>();

                valid_goal_found = false;

                // construct the final plan
                // each endpoint is one possible plan to reach the goal
                // for now just pick the first found
                for (int i = 0; i < plan_endpoints.Count; ++i)
                {
                    List<PlannerNode> plan = new List<PlannerNode>();   
                    PlannerNode node = plan_endpoints[i];

                    if (node.invalid_path == true)
                    {
                        // preconditions could not be solved ...
                        tried_goals.Add(world_state.goal.type);
                        continue;
                    }
                    else
                    {
                        valid_goal_found = true; 
                    }

                    while (node.parent != null)
                    {
                        plan.Add(node);
                        node = node.parent;
                    }

                    output.Add(plan);
                }

                if (valid_goal_found)
                {
                    return output;
                }
                
            } while (valid_goal_found == false);

            return null;
        }

        private bool FindGoalInList(List<PlannerActionEffectType> tried_goals, PlannerActionEffectType plannerActionEffectType)
        {
            for (int i = 0; i < tried_goals.Count; ++i)
            {
                if (tried_goals[i] == plannerActionEffectType)
                {
                    return true;
                }
            }

            return false;
        }

        private bool IsActionAvailabletoSolveGoal(PlannerActionEffect plannerActionEffect, List<PlannerAction> action_list)
        {
            for (int i = 0; i < action_list.Count; ++i)
            {
                for( int e = 0; e < action_list[i].effects.Count; ++e ) 
                {
                    if (action_list[i].effects[e].type == plannerActionEffect.type)
                    {
                        switch (plannerActionEffect.type)
                        {
                            case PlannerActionEffectType.SatedChange:
                                if (action_list[i].effects[e].sated_effect > 0) return true;
                                break;
                            case PlannerActionEffectType.GroceriesChange:
                                if (action_list[i].effects[e].grocieries_effect > 0) return true;
                                break;

                            case PlannerActionEffectType.MoneyChange:
                                if (action_list[i].effects[e].money_effect > 0) return true;
                                break;

                            case PlannerActionEffectType.RestedChange:
                                if (action_list[i].effects[e].rested_effect > 0) return true;
                                break;

                            case PlannerActionEffectType.RelaxedChange:
                                if (action_list[i].effects[e].relaxed_effect > 0) return true;
                                break;

                            case PlannerActionEffectType.EnjoyedEffect:
                                if (action_list[i].effects[e].enjoyed_effect > 0) return true;
                                break;

                            case PlannerActionEffectType.FitChange:
                                if (action_list[i].effects[e].fit_effect > 0) return true;
                                break;

                            default:
                                Debug.Assert(false, "solving for action not yet supported");
                                break;
                        }
                    }
                }
            }

            return false;
        }

        private void FindEnpoint(PlannerNode node)
        {
            if (node.childs.Count == 0)
            {
                // found an endpoint
                this.plan_endpoints.Add(node);
                return;
            }

            for (int i = 0; i < node.childs.Count; ++i)
            {
                FindEnpoint(node.childs[i]);
            }
        }

        private void AddNextNode(PlannerNode parent, PlannerWorldState initial_state, PlannerWorldState current_state)
        {
            // do we have any preconditions left ?

            // always fursly fullfill the location preconditions
            int location_change_precon_id = FindLocationPrecondition(current_state.preconditions);

            if (location_change_precon_id >= 0)
            {
                // found a precondition node for a location change
                // fullfill this first

                PlannerAction goto_action = new PlannerAction(PlannerActionType.GoTo);
                goto_action.effects.Add(PlannerActionEffect.CreateLocationChange(current_state.preconditions[location_change_precon_id].location));

                PlannerNode new_node = new PlannerNode(goto_action, parent);
                parent.childs.Add(new_node);

                PlannerWorldState new_world_state = PlannerWorldState.UpdateStateWithAction(new_node, current_state, goto_action);

                AddNextNode(new_node, initial_state, new_world_state);
                return;
            }

            // now solve all other preconditions
            for (int p = 0; p < current_state.preconditions.Count; ++p)
            {
                PlannerPrecondition precondition = current_state.preconditions[p];
                PlannerActionEffectType goal_type = PlannerActionEffectType.GroceriesChange;
                PlannerActionEffect effect = null;

                if (precondition.type == PlannerPreconditionType.Groceries)
                {
                    goal_type = PlannerActionEffectType.GroceriesChange;
                    effect = PlannerActionEffect.CreateGroceriesChange(
                        Math.Abs(current_state.groceries));
                }
                else if (precondition.type == PlannerPreconditionType.Money)
                {
                    goal_type = PlannerActionEffectType.MoneyChange;

                    effect = PlannerActionEffect.MoneyChange(
                        Math.Abs(current_state.money));
                }
                else
                {
                    Debug.Assert(false, "precondition not yet implemented be solved yet");
                }

                EvaluateActionsForGoal( effect, current_state, initial_state, parent);

                return;
            }

            // have we reached the goal?
            if (current_state.goal.type == PlannerActionEffectType.SatedChange)
            {
                float expected_value = (initial_state.sated + initial_state.goal.sated_effect);
                if (current_state.sated >= expected_value)
                {
                    return;
                }
            }
            else if (current_state.goal.type == PlannerActionEffectType.MoneyChange)
            {
                int expected_value = (initial_state.money + initial_state.goal.money_effect);

                if (current_state.money >= expected_value)
                {
                    return;
                }
            }
            else if (current_state.goal.type == PlannerActionEffectType.RestedChange)
            {
                float expected_value = initial_state.rested + initial_state.goal.rested_effect;

                if (current_state.rested >= expected_value)
                {
                    return;
                }
            }
            else if (current_state.goal.type == PlannerActionEffectType.RelaxedChange)
            {
                float expected_value = initial_state.relaxed + initial_state.goal.relaxed_effect;

                if (current_state.relaxed >= expected_value)
                {
                    return;
                }
            }
            else if (current_state.goal.type == PlannerActionEffectType.EnjoyedEffect)
            {
                float expected_value = initial_state.enjoyed + initial_state.goal.enjoyed_effect;

                if (current_state.enjoyed >= expected_value)
                {
                    return;
                }
            }
            else if (current_state.goal.type == PlannerActionEffectType.FitChange)
            {
                float expected_value = initial_state.fit + initial_state.goal.fit_effect;

                if (current_state.fit >= expected_value)
                {
                    return;
                }
            }
            else
            {
                Debug.Assert(false, "current goal reached is not yet implemented");
            }

            EvaluateActionsForGoal(current_state.goal, current_state, initial_state, parent);
        }

        private void EvaluateActionsForGoal(PlannerActionEffect goal, PlannerWorldState current_state, PlannerWorldState initial_state, PlannerNode parent)
        {
            bool node_found = false;

            // check which action resolves the precondition
            for (int i = 0; i < action_list.Count; ++i)
            {
                PlannerAction possible_action = action_list[i];

                for (int e = 0; e < possible_action.effects.Count; ++e)
                {

                    if (goal.type == PlannerActionEffectType.GroceriesChange)
                    {
                        if (possible_action.effects[e].type == PlannerActionEffectType.GroceriesChange
                            && possible_action.effects[e].grocieries_effect > 0)
                        {
                            // found a valid node, add it to the graph
                            PlannerNode new_node = null;
                            PlannerWorldState new_world_state = current_state;

                            PlannerNode local_parent = parent;

                            int expected_value = current_state.groceries + goal.grocieries_effect;

                            do
                            {
                                new_node = new PlannerNode(possible_action, local_parent);
                                local_parent.childs.Add(new_node);
                                new_world_state = PlannerWorldState.UpdateStateWithAction(new_node, new_world_state, possible_action);

                                local_parent = new_node;

                            } while (new_world_state.groceries < expected_value);

                            // assume the intial goal has been solved by now
                            // not resetting the state could cause later iteration to trigger the action again
                            // in the current backwards tracking of world states this is not easily supported
                            // we would need to go back to last action which solved this goal,
                            // change thw world state and start again
                            // => we don't do this and at a later stage, after the plan has been created
                            // the used might decide that the small varation is interesting to him
                            // and can therfoe start another plan calculation taking this into consideration
                            if (initial_state.goal.type == goal.type)
                            {
                                initial_state.goal.grocieries_effect = 0;
                            }

                            AddNextNode(new_node, initial_state, new_world_state);

                            node_found = true;
                        }
                    } else if (goal.type == PlannerActionEffectType.SatedChange) {
                        if (possible_action.effects[e].type == PlannerActionEffectType.SatedChange
                            && possible_action.effects[e].sated_effect > 0)
                        {
                            // found a valid node, add it to the graph
                            PlannerNode new_node = null;
                            PlannerWorldState new_world_state = current_state;

                            PlannerNode local_parent = parent;

                            float expected_value = current_state.sated + goal.sated_effect;

                            do
                            {
                                new_node = new PlannerNode(possible_action, local_parent);
                                local_parent.childs.Add(new_node);
                                new_world_state = PlannerWorldState.UpdateStateWithAction(new_node, new_world_state, possible_action);

                                local_parent = new_node;

                            } while (new_world_state.sated < expected_value);

                            // assume the intial goal has been solved by now
                            // not resetting the state could cause later iteration to trigger the action again
                            // in the current backwards tracking of world states this is not easily supported
                            // we would need to go back to last action which solved this goal,
                            // change thw world state and start again
                            // => we don't do this and at a later stage, after the plan has been created
                            // the used might decide that the small varation is interesting to him
                            // and can therfoe start another plan calculation taking this into consideration
                            if (initial_state.goal.type == goal.type)
                            {
                                initial_state.goal.sated_effect = 0;
                            }

                            AddNextNode(new_node, initial_state, new_world_state);

                            node_found = true;
                        }
                    }
                    else if (goal.type == PlannerActionEffectType.MoneyChange)
                    {
                        if (possible_action.effects[e].type == PlannerActionEffectType.MoneyChange
                            && possible_action.effects[e].money_effect > 0)
                        {
                            // found a valid node, add it to the graph
                            PlannerNode new_node = null;
                            PlannerWorldState new_world_state = current_state;

                            PlannerNode local_parent = parent;

                            int expected_value = current_state.money + goal.money_effect;

                            do
                            {
                                new_node = new PlannerNode(possible_action, local_parent);
                                local_parent.childs.Add(new_node);
                                new_world_state = PlannerWorldState.UpdateStateWithAction(new_node, new_world_state, possible_action);

                                local_parent = new_node;

                            } while (new_world_state.money < expected_value);

                            // assume the intial goal has been solved by now
                            // not resetting the state could cause later iteration to trigger the action again
                            // in the current backwards tracking of world states this is not easily supported
                            // we would need to go back to last action which solved this goal,
                            // change thw world state and start again
                            // => we don't do this and at a later stage, after the plan has been created
                            // the used might decide that the small varation is interesting to him
                            // and can therfoe start another plan calculation taking this into consideration
                            if (initial_state.goal.type == goal.type)
                            {
                                initial_state.goal.money_effect = 0;
                            }

                            AddNextNode(new_node, initial_state, new_world_state);

                            node_found = true;
                        }

                    }
                    else if (goal.type == PlannerActionEffectType.RestedChange)
                    {
                        if (possible_action.effects[e].type == PlannerActionEffectType.RestedChange
                            && possible_action.effects[e].rested_effect > 0)
                        {
                            // found a valid node, add it to the graph
                            PlannerNode new_node = null;
                            PlannerWorldState new_world_state = current_state;

                            PlannerNode local_parent = parent;

                            float expected_value = current_state.rested + goal.rested_effect;

                            do
                            {
                                new_node = new PlannerNode(possible_action, local_parent);
                                local_parent.childs.Add(new_node);
                                new_world_state = PlannerWorldState.UpdateStateWithAction(new_node, new_world_state, possible_action);

                                local_parent = new_node;

                            } while (new_world_state.rested < expected_value);

                            // assume the intial goal has been solved by now
                            // not resetting the state could cause later iteration to trigger the action again
                            // in the current backwards tracking of world states this is not easily supported
                            // we would need to go back to last action which solved this goal,
                            // change thw world state and start again
                            // => we don't do this and at a later stage, after the plan has been created
                            // the used might decide that the small varation is interesting to him
                            // and can therfoe start another plan calculation taking this into consideration
                            if (initial_state.goal.type == goal.type)
                            {
                                initial_state.goal.rested_effect = 0;
                            }

                            AddNextNode(new_node, initial_state, new_world_state);

                            node_found = true;
                        }

                    }
                    else if (goal.type == PlannerActionEffectType.RelaxedChange)
                    {
                        if (possible_action.effects[e].type == PlannerActionEffectType.RelaxedChange
                            && possible_action.effects[e].relaxed_effect > 0)
                        {
                            // found a valid node, add it to the graph
                            PlannerNode new_node = null;
                            PlannerWorldState new_world_state = current_state;

                            PlannerNode local_parent = parent;

                            float expected_value = current_state.relaxed + goal.relaxed_effect;

                            do
                            {
                                new_node = new PlannerNode(possible_action, local_parent);
                                local_parent.childs.Add(new_node);
                                new_world_state = PlannerWorldState.UpdateStateWithAction(new_node, new_world_state, possible_action);

                                local_parent = new_node;

                            } while (new_world_state.relaxed < expected_value);

                            // assume the intial goal has been solved by now
                            // not resetting the state could cause later iteration to trigger the action again
                            // in the current backwards tracking of world states this is not easily supported
                            // we would need to go back to last action which solved this goal,
                            // change thw world state and start again
                            // => we don't do this and at a later stage, after the plan has been created
                            // the used might decide that the small varation is interesting to him
                            // and can therfoe start another plan calculation taking this into consideration
                            if (initial_state.goal.type == goal.type)
                            {
                                initial_state.goal.relaxed_effect = 0;
                            }

                            AddNextNode(new_node, initial_state, new_world_state);

                            node_found = true;
                        }

                    }
                    else if (goal.type == PlannerActionEffectType.EnjoyedEffect)
                    {
                        if (possible_action.effects[e].type == PlannerActionEffectType.EnjoyedEffect
                            && possible_action.effects[e].enjoyed_effect > 0)
                        {
                            // found a valid node, add it to the graph
                            PlannerNode new_node = null;
                            PlannerWorldState new_world_state = current_state;

                            PlannerNode local_parent = parent;

                            float expected_value = current_state.enjoyed + goal.enjoyed_effect;

                            do
                            {
                                new_node = new PlannerNode(possible_action, local_parent);
                                local_parent.childs.Add(new_node);
                                new_world_state = PlannerWorldState.UpdateStateWithAction(new_node, new_world_state, possible_action);

                                local_parent = new_node;

                            } while (new_world_state.enjoyed < expected_value);

                            // assume the intial goal has been solved by now
                            // not resetting the state could cause later iteration to trigger the action again
                            // in the current backwards tracking of world states this is not easily supported
                            // we would need to go back to last action which solved this goal,
                            // change thw world state and start again
                            // => we don't do this and at a later stage, after the plan has been created
                            // the used might decide that the small varation is interesting to him
                            // and can therfoe start another plan calculation taking this into consideration
                            if (initial_state.goal.type == goal.type)
                            {
                                initial_state.goal.enjoyed_effect = 0;
                            }

                            AddNextNode(new_node, initial_state, new_world_state);

                            node_found = true;
                        }

                    }
                    else if (goal.type == PlannerActionEffectType.FitChange)
                    {
                        if (possible_action.effects[e].type == PlannerActionEffectType.FitChange
                            && possible_action.effects[e].fit_effect > 0)
                        {
                            // found a valid node, add it to the graph
                            PlannerNode new_node = null;
                            PlannerWorldState new_world_state = current_state;

                            PlannerNode local_parent = parent;

                            float expected_value = current_state.fit + goal.fit_effect;

                            do
                            {
                                new_node = new PlannerNode(possible_action, local_parent);
                                local_parent.childs.Add(new_node);
                                new_world_state = PlannerWorldState.UpdateStateWithAction(new_node, new_world_state, possible_action);

                                local_parent = new_node;

                            } while (new_world_state.fit < expected_value);

                            // assume the intial goal has been solved by now
                            // not resetting the state could cause later iteration to trigger the action again
                            // in the current backwards tracking of world states this is not easily supported
                            // we would need to go back to last action which solved this goal,
                            // change thw world state and start again
                            // => we don't do this and at a later stage, after the plan has been created
                            // the used might decide that the small varation is interesting to him
                            // and can therfoe start another plan calculation taking this into consideration
                            if (initial_state.goal.type == goal.type)
                            {
                                initial_state.goal.fit_effect = 0;
                            }

                            AddNextNode(new_node, initial_state, new_world_state);

                            node_found = true;
                        }

                    }
                    else {
                        Debug.Assert(false, "goal not yet supported");
                    }
                }
            }

            if (node_found == false && goal.type == PlannerActionEffectType.GroceriesChange)
            {
                // this can happen, if the store is closed
                // invalidate this path
                parent.invalid_path = true;
            }
            else if (node_found == false && goal.type == PlannerActionEffectType.MoneyChange)
            {
                // this can happen, if the money is required but the work place is closed
                // invalidate this path
                parent.invalid_path = true;
            }
            else
            {
                Debug.Assert(node_found == true, "failed to find a node which support the requested goal");
            }
        }

        private int FindLocationPrecondition(List<PlannerPrecondition> list)
        {
            for (int i = 0; i < list.Count; ++i)
            {
                if (list[i].type == PlannerPreconditionType.Location)
                {
                    return i;
                }
            }

            return -1;
        }
    }
}
